﻿
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Timers;
using CatEars.Core.Numbers;
using CatEars.Text;

namespace CatEars.Core.Net
{
#pragma warning disable 1591
    public class NetworkMonitor : IDisposable
    {
        public class NetworkAdapter
        {
            public string Name { get; private set; }

            private PerformanceCounter _downloadCounter;
            private PerformanceCounter _uploadCounter;

            internal NetworkAdapter(string name,
                PerformanceCounter downloadCounter,
                PerformanceCounter uploadCounter)
            {
                this.Name = name;
                DisplayFormat = "Download:{0}  Upload:{1}";
                _downloadCounter = downloadCounter;
                _uploadCounter = uploadCounter;
            }

            public long DownloadSpeed { get; private set; }
            public long UploadSpeed { get; private set; }

            public double DownloadSpeedKbps { get { return this.DownloadSpeed / 1024.0; } }
            public double UploadSpeedKbps { get { return this.UploadSpeed / 1024.0; } }

            public override string ToString() { return this.Name; }

            public string DisplayFormat{get;set;}
            public double DisplayDiv { get; set; }

            #region 获取用于显示的文本
            public string GetSpeedDisplayText()
            {
                return GetSpeedDisplayText(DisplayFormat, DisplayDiv);
            }

            public string GetSpeedDisplayText(string strFormat, double dblDiv)
            {
                if (string.IsNullOrEmpty(strFormat)) strFormat = DisplayFormat;
                return GetSpeedDisplayText(strFormat, dblDiv, DownloadSpeed, UploadSpeed);
            }

            public static string GetSpeedDisplayText(ICollection<NetworkAdapter> adapters,
                string strFormat = null,
                double dblDiv = 0)
            {
                double dblDownloadValue = 0;
                double dblUploadValue = 0;
                if (adapters != null)
                {
                    foreach (var item in adapters)
                    {
                        dblDownloadValue += item.DownloadSpeed;
                        dblUploadValue += item.UploadSpeed;
                    }
                }
                return GetSpeedDisplayText(strFormat, dblDiv, dblDownloadValue, dblUploadValue);
            }

            static string GetSpeedDisplayText(string strFormat, double dblDiv,
                double dblDownloadValue, double dblUploadValue)
            {
                if (string.IsNullOrEmpty(strFormat)) strFormat = "Download:{0}  Upload:{1}";
                if (dblDiv > 1)
                {
                    return string.Format(strFormat, dblDownloadValue / dblDiv, dblUploadValue / dblDiv);
                }
                else
                {
                    return string.Format(strFormat,
                        Display.DisplayNumber(dblDownloadValue, UnitConv.ConvByte.Instance, "B"),
                        Display.DisplayNumber(dblUploadValue, UnitConv.ConvByte.Instance, "B"));
                }
            }
            #endregion

            private long _lngDownValueOld;
            private long _lngUpValueOld;

            public void Init()
            {
                _lngDownValueOld = _downloadCounter.NextSample().RawValue;
                _lngUpValueOld = _uploadCounter.NextSample().RawValue;
            }

            internal void Refresh(int intInterval)
            {
                var lngDownloadValue = _downloadCounter.NextSample().RawValue;
                var lngUploadValue = _uploadCounter.NextSample().RawValue;

                DownloadSpeed = (lngDownloadValue - _lngDownValueOld) * 1000 / intInterval;
                _lngDownValueOld = lngDownloadValue;

                UploadSpeed = (lngUploadValue - _lngUpValueOld) * 1000 / intInterval;
                _lngUpValueOld = lngUploadValue;
            }

            internal void Refresh()
            {
                Refresh(1000);
            }
        }

        private Timer _timer;
        private List<NetworkAdapter> _adapters;
        private HashSet<NetworkAdapter> _monitoredAdapters;

        public ReadOnlyCollection<NetworkAdapter> Adapters { get { return _adapters.AsReadOnly(); } }

        public event EventHandler SpeedUpdated;

        public NetworkMonitor() : this(1000) { }

        public NetworkMonitor(int intInterval)
        {
            EnumerateNetworkAdapters();

            Interval = intInterval;
            _timer = new Timer(intInterval);
            _timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
        }

        public void Dispose()
        {
            if (_timer != null)
            {
                _timer.Dispose();
                _timer = null;
            }
        }

        private void EnumerateNetworkAdapters()
        {
            _adapters = new List<NetworkAdapter>();
            _monitoredAdapters = new HashSet<NetworkAdapter>();
            PerformanceCounterCategory category = new PerformanceCounterCategory("Network Interface");
            string[] strInstanceNames = category.GetInstanceNames();
            foreach (string name in strInstanceNames)
            {
                if (name == "MS TCP Loopback interface") continue;

                NetworkAdapter adapter = new NetworkAdapter(name,
                    new PerformanceCounter("Network Interface", "Bytes Received/sec", name),
                    new PerformanceCounter("Network Interface", "Bytes Sent/sec", name));
                _adapters.Add(adapter);
            }
            _adapters.TrimExcess();
        }

        public int Interval { get; private set; }

        private void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                if (_monitoredAdapters != null && _monitoredAdapters.Count > 0)
                {
                    foreach (NetworkAdapter adapter in _monitoredAdapters)
                    {
                        adapter.Refresh(Interval);
                    }
                    if (SpeedUpdated != null)
                    {
                        SpeedUpdated(this, EventArgs.Empty);
                    }
                }
            }
            catch
            {
                //由于没有加锁 _monitoredAdapters集合变化可能会报错 忽略
            }
        }

        public void StartMonitoring()
        {
            lock (_monitoredAdapters)
            {
                if (_adapters.Count > 0)
                {
                    foreach (NetworkAdapter adapter in _adapters)
                    {
                        if (!_monitoredAdapters.Contains(adapter))
                        {
                            _monitoredAdapters.Add(adapter);
                            adapter.Init();
                        }
                    }
                    _timer.Enabled = true;
                }
            }
        }

        public void StartMonitoring(NetworkAdapter adapter)
        {
            lock (_monitoredAdapters)
            {
                if (!_monitoredAdapters.Contains(adapter))
                {
                    _monitoredAdapters.Add(adapter);
                    adapter.Init();
                }
                _timer.Enabled = true;
            }
        }

        public void StopMonitoring()
        {
            lock (_monitoredAdapters)
            {
                _monitoredAdapters.Clear();
                _timer.Enabled = false;
            }
        }

        public void StopMonitoring(NetworkAdapter adapter)
        {
            lock (_monitoredAdapters)
            {
                this._monitoredAdapters.Remove(adapter);
                if (_monitoredAdapters.Count == 0) _timer.Enabled = false;
            }
        }

        /// <summary>
        /// 获取瞬时产生流量最大的网卡
        /// </summary>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public NetworkAdapter GetMainNetworkAdapter(NetworkAdapter defaultValue = null)
        {
            NetworkAdapter result = defaultValue;
            long lngMax = 0;
            foreach (var item in _monitoredAdapters)
            {
                long lngTmp = item.DownloadSpeed + item.UploadSpeed;
                if (lngTmp > lngMax)
                {
                    lngMax = lngTmp;
                    result = item;
                }
            }
            return result;
        }

        /// <summary>
        /// 生成显示用的文本
        /// </summary>
        /// <param name="separator"></param>
        /// <param name="strFormat"></param>
        /// <param name="dblDiv"></param>
        /// <returns></returns>
        public string GetDisplayText(string separator = "\n",
            string strFormat = null,
            double dblDiv = 0)
        {
            List<string> lstText = new List<string>();
            foreach (var item in Adapters)
            {
                string strText = item.Name + " " + item.GetSpeedDisplayText(strFormat, dblDiv);
                lstText.Add(strText);
            }
            return string.Join(separator, lstText);
        }
    }
}
